home *** CD-ROM | disk | FTP | other *** search
/ Java Programmer's Toolkit / Java Programmer's Toolkit.iso / src / java / awt / mediat~1.jav < prev    next >
Encoding:
Text File  |  1996-01-12  |  18.0 KB  |  641 lines

  1. /*
  2.  * @(#)MediaTracker.java    1.14 95/12/14 Jim Graham
  3.  *
  4.  * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, and distribute this software
  7.  * and its documentation for NON-COMMERCIAL purposes and without
  8.  * fee is hereby granted provided that this copyright notice
  9.  * appears in all copies. Please refer to the file "copyright.html"
  10.  * for further important copyright and licensing information.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  13.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  14.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  15.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  16.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  17.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  18.  */
  19.  
  20. package java.awt;
  21.  
  22. import java.awt.Component;
  23. import java.awt.Image;
  24. import java.awt.Graphics;
  25. import java.awt.image.ImageObserver;
  26.  
  27. /**
  28.  * A utility class to track the status of a number of media objects.
  29.  * Media objects could include images as well as audio clips, though
  30.  * currently only images are supported.  To use it, simply create an
  31.  * instance and then call addImage() for each image to be tracked.
  32.  * Each image can be assigned a unique ID for indentification purposes.
  33.  * The IDs control the priority order in which the images are fetched
  34.  * as well as identifying unique subsets of the images that can be
  35.  * waited on independently.  Here is an example:
  36.  * <pre>
  37.  *
  38.  * import java.applet.Applet;
  39.  * import java.awt.Color;
  40.  * import java.awt.Image;
  41.  * import java.awt.Graphics;
  42.  * import java.awt.MediaTracker;
  43.  *
  44.  * public class ImageBlaster extends Applet implements Runnable {
  45.  *    MediaTracker tracker;
  46.  *    Image bg;
  47.  *    Image anim[] = new Image[5];
  48.  *    int index;
  49.  *    Thread animator;
  50.  *
  51.  *    // Get the images for the background (id == 0) and the animation
  52.  *    // frames (id == 1) and add them to the MediaTracker
  53.  *    public void init() {
  54.  *        tracker = new MediaTracker(this);
  55.  *        bg = getImage(getDocumentBase(), "images/background.gif");
  56.  *        tracker.addImage(bg, 0);
  57.  *        for (int i = 0; i < 5; i++) {
  58.  *        anim[i] = getImage(getDocumentBase(), "images/anim"+i+".gif");
  59.  *        tracker.addImage(anim[i], 1);
  60.  *        }
  61.  *    }
  62.  *    // Start the animation thread.
  63.  *    public void start() {
  64.  *        animator = new Thread(this);
  65.  *        animator.start();
  66.  *    }
  67.  *    // Stop the animation thread.
  68.  *    public void stop() {
  69.  *        animator.stop();
  70.  *        animator = null;
  71.  *    }
  72.  *    // Run the animation thread.
  73.  *    // First wait for the background image to fully load and paint.
  74.  *    // Then wait for all of the animation frames to finish loading.
  75.  *    // Finally loop and increment the animation frame index.
  76.  *    public void run() {
  77.  *        try {
  78.  *        tracker.waitForID(0);
  79.  *        tracker.waitForID(1);
  80.  *        } catch (InterruptedException e) {
  81.  *        return;
  82.  *        }
  83.  *        Thread me = Thread.currentThread();
  84.  *        while (animator == me) {
  85.  *        try {
  86.  *            Thread.sleep(100);
  87.  *        } catch (InterruptedException e) {
  88.  *            break;
  89.  *        }
  90.  *        synchronized (this) {
  91.  *            index++;
  92.  *            if (index >= anim.length) {
  93.  *            index = 0;
  94.  *            }
  95.  *        }
  96.  *        repaint();
  97.  *        }
  98.  *    }
  99.  *    // The background image fills our frame so we don't need to clear
  100.  *    // the applet on repaints, just call the paint method.
  101.  *    public void update(Graphics g) {
  102.  *        paint(g);
  103.  *    }
  104.  *    // Paint a large red rectangle if there are any errors loading the
  105.  *    // images.  Otherwise always paint the background so that it appears
  106.  *    // incrementally as it is loading.  Finally, only paint the current
  107.  *    // animation frame if all of the frames (id == 1) are done loading
  108.  *    // so that we don't get partial animations.
  109.  *    public void paint(Graphics g) {
  110.  *        if ((tracker.statusAll() & MediaTracker.ERRORED) != 0) {
  111.  *        g.setColor(Color.red);
  112.  *        g.fillRect(0, 0, size().width, size().height);
  113.  *        return;
  114.  *        }
  115.  *        g.drawImage(bg, 0, 0, this);
  116.  *        if ((tracker.statusID(1) & MediaTracker.COMPLETE) != 0) {
  117.  *        g.drawImage(anim[index], 10, 10, this);
  118.  *        }
  119.  *    }
  120.  * }
  121.  *
  122.  * </pre>
  123.  *
  124.  * @version     1.14, 12/14/95
  125.  * @author     Jim Graham
  126.  */
  127. public class MediaTracker {
  128.     Component target;
  129.     MediaEntry head;
  130.  
  131.     /**
  132.      * Creates a Media tracker to track images for a given Component.
  133.      * @param comp the component on which the images will eventually be drawn
  134.      */
  135.     public MediaTracker(Component comp) {
  136.     target = comp;
  137.     }
  138.  
  139.     /**
  140.      * Adds an image to the list of images being tracked.  The image
  141.      * will eventually be rendered at its default (unscaled) size.
  142.      * @param image the image to be tracked
  143.      * @param id the identifier used to later track this image
  144.      */
  145.     public void addImage(Image image, int id) {
  146.     addImage(image, id, -1, -1);
  147.     }
  148.  
  149.     /**
  150.      * Adds a scaled image to the list of images being tracked.  The
  151.      * image will eventually be rendered at the indicated size.
  152.      * @param image the image to be tracked
  153.      * @param id the identifier used to later track this image
  154.      * @param w the width that the image will be rendered at
  155.      * @param h the height that the image will be rendered at
  156.      */
  157.     public synchronized void addImage(Image image, int id, int w, int h) {
  158.     head = MediaEntry.insert(head,
  159.                  new ImageMediaEntry(this, image, id, w, h));
  160.     }
  161.  
  162.     /**
  163.      * Flag indicating some media is currently being loaded.
  164.      * @see #statusAll
  165.      * @see #statusID
  166.      */
  167.     public static final int LOADING = 1;
  168.  
  169.     /**
  170.      * Flag indicating the download of some media was aborted.
  171.      * @see #statusAll
  172.      * @see #statusID
  173.      */
  174.     public static final int ABORTED = 2;
  175.  
  176.     /**
  177.      * Flag indicating the download of some media encountered an error.
  178.      * @see #statusAll
  179.      * @see #statusID
  180.      */
  181.     public static final int ERRORED = 4;
  182.  
  183.     /**
  184.      * Flag indicating the download of media completed successfully.
  185.      * @see #statusAll
  186.      * @see #statusID
  187.      */
  188.     public static final int COMPLETE = 8;
  189.  
  190.     static final int DONE = (ABORTED | ERRORED | COMPLETE);
  191.  
  192.     /**
  193.      * Checks to see if all images have finished loading but does not start
  194.      * loading the images if they are not already loading.
  195.      * If there is an error while loading or scaling an image then that
  196.      * image is considered "complete."
  197.      * Use isErrorAny() or isErrorID() to check for errors.
  198.      * @return true if all images have finished loading, were aborted or
  199.      * encountered an error
  200.      * @see #checkAll(boolean)
  201.      * @see #checkID
  202.      * @see #isErrorAny
  203.      * @see #isErrorID
  204.      */
  205.     public boolean checkAll() {
  206.     return checkAll(false);
  207.     }
  208.  
  209.     /**
  210.      * Checks to see if all images have finished loading. If load is
  211.      * true, starts loading any images that are not yet being loaded.
  212.      * If there is an error while loading or scaling an image then
  213.      * that image is considered "complete."  Use isErrorAny() or
  214.      * isErrorID() to check for errors.
  215.  
  216.      * @param load start loading the images if this parameter is true
  217.      * @return true if all images have finished loading, were aborted or
  218.      * encountered an error
  219.      * @see #isErrorAny
  220.      * @see #isErrorID
  221.      * @see #checkID(int, boolean)
  222.      * @see #checkAll()
  223.      */
  224.     public synchronized boolean checkAll(boolean load) {
  225.     MediaEntry cur = head;
  226.     boolean done = true;
  227.     while (cur != null) {
  228.         if ((cur.getStatus(load) & DONE) == 0) {
  229.         done = false;
  230.         }
  231.         cur = cur.next;
  232.     }
  233.     return done;
  234.     }
  235.  
  236.     /**
  237.      * Checks the error status of all of the images.
  238.      * @return true if any of the images had an error during loading
  239.      * @see #isErrorID
  240.      * @see #getErrorsAny
  241.      */
  242.     public synchronized boolean isErrorAny() {
  243.     MediaEntry cur = head;
  244.     while (cur != null) {
  245.         if ((cur.getStatus(false) & ERRORED) != 0) {
  246.         return true;
  247.         }
  248.         cur = cur.next;
  249.     }
  250.     return false;
  251.     }
  252.  
  253.     /**
  254.      * Returns a list of all media that have encountered an error.
  255.      * @return an array of media objects or null if there are no errors
  256.      * @see #isErrorAny
  257.      * @see #getErrorsID
  258.      */
  259.     public synchronized Object[] getErrorsAny() {
  260.     MediaEntry cur = head;
  261.     int numerrors = 0;
  262.     while (cur != null) {
  263.         if ((cur.getStatus(false) & ERRORED) != 0) {
  264.         numerrors++;
  265.         }
  266.         cur = cur.next;
  267.     }
  268.     if (numerrors == 0) {
  269.         return null;
  270.     }
  271.     Object errors[] = new Object[numerrors];
  272.     cur = head;
  273.     numerrors = 0;
  274.     while (cur != null) {
  275.         if ((cur.getStatus(false) & ERRORED) != 0) {
  276.         errors[numerrors++] = cur.getMedia();
  277.         }
  278.         cur = cur.next;
  279.     }
  280.     return errors;
  281.     }
  282.  
  283.     /**
  284.      * Starts loading all images. Waits until they have finished loading,
  285.      * are aborted, or it receives an error.
  286.      * If there is an error while loading or scaling an image then that
  287.      * image is considered "complete."
  288.      * Use isErrorAny() or statusAll() to check for errors.
  289.      * @exception InterruptedException 
  290.      *            Another thread has interrupted this thread. 
  291.      * @see #waitForID
  292.      * @see #waitForAll(long)
  293.      * @see #isErrorAny
  294.      * @see #isErrorID
  295.      */
  296.     public void waitForAll() throws InterruptedException {
  297.     waitForAll(0);
  298.     }
  299.  
  300.     /**
  301.      * Starts loading all images. Waits until they have finished loading,
  302.      * are aborted, it receives an error, or until the specified timeout has
  303.      * elapsed.
  304.      * If there is an error while loading or scaling an image then that
  305.      * image is considered "complete."
  306.      * Use isErrorAny() or statusAll() to check for errors.
  307.      * @param ms the length of time to wait for the loading to complete
  308.      * @return true if all images were successfully loaded
  309.      * @see #waitForID
  310.      * @see #waitForAll()
  311.      * @see #isErrorAny
  312.      * @see #isErrorID
  313.      * @exception InterruptedException 
  314.      *            Another thread has interrupted this thread. 
  315.      */
  316.     public synchronized boolean waitForAll(long ms)
  317.     throws InterruptedException
  318.     {
  319.     long end = System.currentTimeMillis() + ms;
  320.     boolean first = true;
  321.     while (true) {
  322.         int status = statusAll(first);
  323.         if ((status & LOADING) == 0) {
  324.         return (status == COMPLETE);
  325.         }
  326.         first = false;
  327.         long timeout;
  328.         if (ms == 0) {
  329.         timeout = 0;
  330.         } else {
  331.         timeout = end - System.currentTimeMillis();
  332.         if (timeout <= 0) {
  333.             return false;
  334.         }
  335.         }
  336.         wait(timeout);
  337.     }
  338.     }
  339.  
  340.     /**
  341.      * Returns the boolean OR of the status of all of the media being
  342.      * tracked.
  343.      * @param load specifies whether to start the media loading
  344.      * @see #statusID
  345.      * @see #LOADING
  346.      * @see #ABORTED
  347.      * @see #ERRORED
  348.      * @see #COMPLETE
  349.      */
  350.     public int statusAll(boolean load) {
  351.     MediaEntry cur = head;
  352.     int status = 0;
  353.     while (cur != null) {
  354.         status = status | cur.getStatus(load);
  355.         cur = cur.next;
  356.     }
  357.     return status;
  358.     }
  359.  
  360.     /**
  361.      * Checks to see if all images tagged with the indicated ID have
  362.      * finished loading, but does not start loading the images if they
  363.      * are not already loading.
  364.      * If there is an error while loading or scaling an image then that
  365.      * image is considered "complete."
  366.      * Use isErrorAny() or isErrorID() to check for errors.
  367.      * @param id the identifier used to determine which images to check
  368.      * @return true if all tagged images have finished loading, were
  369.      * aborted, or an error occurred.
  370.      * @see #checkID(int, boolean)
  371.      * @see #checkAll
  372.      * @see #isErrorAny
  373.      * @see #isErrorID
  374.      */
  375.     public boolean checkID(int id) {
  376.     return checkID(id, false);
  377.     }
  378.  
  379.     /**
  380.      * Checks to see if all images tagged with the indicated ID have
  381.      * finished loading. If load is true, starts loading any images
  382.      * with that ID that are not yet being loaded.  If there is an
  383.      * error while loading or scaling an image then that image is
  384.      * considered "complete."  Use isErrorAny() or isErrorID() to
  385.      * check for errors.
  386.  
  387.      * @param id the identifier used to determine which images to check
  388.      * @param load start loading the images if this parameter is true
  389.      * @return true if all tagged images have finished loading, were
  390.      * aborted, or an error occurred
  391.      * @see #checkID(int)
  392.      * @see #checkAll
  393.      * @see #isErrorAny
  394.      * @see #isErrorID
  395.      */
  396.     public synchronized boolean checkID(int id, boolean load) {
  397.     MediaEntry cur = head;
  398.     boolean done = true;
  399.     while (cur != null) {
  400.         if (cur.getID() == id && (cur.getStatus(load) & DONE) == 0) {
  401.         done = false;
  402.         }
  403.         cur = cur.next;
  404.     }
  405.     return done;
  406.     }
  407.  
  408.     /**
  409.      * Checks the error status of all of the images with the specified ID.
  410.      * @param id the identifier used to determine which images to check
  411.      * @return true if any of the tagged images had an error during loading
  412.      * @see #isErrorAny
  413.      * @see #getErrorsID
  414.      */
  415.     public synchronized boolean isErrorID(int id) {
  416.     MediaEntry cur = head;
  417.     while (cur != null) {
  418.         if (cur.getID() == id && (cur.getStatus(false) & ERRORED) != 0) {
  419.         return true;
  420.         }
  421.         cur = cur.next;
  422.     }
  423.     return false;
  424.     }
  425.  
  426.     /**
  427.      * Returns a list of media with the specified ID that have encountered
  428.      * an error.
  429.      * @param id the identifier used to determine which images to return
  430.      * @return an array of media objects or null if there are no errors
  431.      * @see #isErrorID
  432.      * @see #getErrorsAny
  433.      */
  434.     public synchronized Object[] getErrorsID(int id) {
  435.     MediaEntry cur = head;
  436.     int numerrors = 0;
  437.     while (cur != null) {
  438.         if (cur.getID() == id && (cur.getStatus(false) & ERRORED) != 0) {
  439.         numerrors++;
  440.         }
  441.         cur = cur.next;
  442.     }
  443.     if (numerrors == 0) {
  444.         return null;
  445.     }
  446.     Object errors[] = new Object[numerrors];
  447.     cur = head;
  448.     numerrors = 0;
  449.     while (cur != null) {
  450.         if (cur.getID() == id && (cur.getStatus(false) & ERRORED) != 0) {
  451.         errors[numerrors++] = cur.getMedia();
  452.         }
  453.         cur = cur.next;
  454.     }
  455.     return errors;
  456.     }
  457.  
  458.     /**
  459.      * Starts loading all images with the specified ID and waits until they
  460.      * have finished loading or receive an error.
  461.      * If there is an error while loading or scaling an image then that
  462.      * image is considered "complete."
  463.      * Use statusID() or isErrorID() to check for errors.
  464.      * @param id the identifier used to determine which images to wait for
  465.      * @see #waitForAll
  466.      * @see #waitForID(int, long)
  467.      * @see #isErrorAny
  468.      * @see #isErrorID
  469.      * @exception InterruptedException 
  470.      *            Another thread has interrupted this thread. 
  471.      */
  472.     public void waitForID(int id) throws InterruptedException {
  473.     waitForID(id, 0);
  474.     }
  475.  
  476.     /**
  477.      * Starts loading all images with the specified ID. Waits until they
  478.      * have finished loading, an error occurs, or the specified
  479.      * timeout has elapsed.
  480.      * If there is an error while loading or scaling an image then that
  481.      * image is considered "complete."
  482.      * Use statusID or isErrorID to check for errors.
  483.      * @param id the identifier used to determine which images to wait for
  484.      * @param ms the length of time to wait for the loading to complete
  485.      * @see #waitForAll
  486.      * @see #waitForID(int)
  487.      * @see #isErrorAny
  488.      * @see #isErrorID
  489.      * @exception InterruptedException 
  490.      *            Another thread has interrupted this thread. 
  491.      */
  492.     public synchronized boolean waitForID(int id, long ms)
  493.     throws InterruptedException
  494.     {
  495.     long end = System.currentTimeMillis() + ms;
  496.     boolean first = true;
  497.     while (true) {
  498.         int status = statusID(id, first);
  499.         if ((status & LOADING) == 0) {
  500.         return (status == COMPLETE);
  501.         }
  502.         first = false;
  503.         long timeout;
  504.         if (ms == 0) {
  505.         timeout = 0;
  506.         } else {
  507.         timeout = end - System.currentTimeMillis();
  508.         if (timeout <= 0) {
  509.             return false;
  510.         }
  511.         }
  512.         wait(timeout);
  513.     }
  514.     }
  515.  
  516.     /**
  517.      * Returns the boolean OR of the status of all of the media with
  518.      * a given ID.
  519.      * @param id the identifier used to determine which images to check
  520.      * @param load specifies whether to start the media loading
  521.      * @see #statusAll
  522.      * @see #LOADING
  523.      * @see #ABORTED
  524.      * @see #ERRORED
  525.      * @see #COMPLETE
  526.      */
  527.     public int statusID(int id, boolean load) {
  528.     MediaEntry cur = head;
  529.     int status = 0;
  530.     while (cur != null) {
  531.         if (cur.getID() == id) {
  532.         status = status | cur.getStatus(load);
  533.         }
  534.         cur = cur.next;
  535.     }
  536.     return status;
  537.     }
  538.  
  539.     synchronized void setDone() {
  540.     notifyAll();
  541.     }
  542. }
  543.  
  544. abstract class MediaEntry {
  545.     MediaTracker tracker;
  546.     int ID;
  547.     MediaEntry next;
  548.  
  549.     int status;
  550.  
  551.     MediaEntry(MediaTracker mt, int id) {
  552.     tracker = mt;
  553.     ID = id;
  554.     }
  555.  
  556.     abstract Object getMedia();
  557.  
  558.     static MediaEntry insert(MediaEntry head, MediaEntry me) {
  559.     MediaEntry cur = head;
  560.     MediaEntry prev = null;
  561.     while (cur != null) {
  562.         if (cur.ID > me.ID) {
  563.         break;
  564.         }
  565.         prev = cur;
  566.         cur = cur.next;
  567.     }
  568.     me.next = cur;
  569.     if (prev == null) {
  570.         head = me;
  571.     } else {
  572.         prev.next = me;
  573.     }
  574.     return head;
  575.     }
  576.  
  577.     int getID() {
  578.     return ID;
  579.     }
  580.  
  581.     abstract void startLoad();
  582.  
  583.     static final int LOADING = MediaTracker.LOADING;
  584.     static final int ABORTED = MediaTracker.ABORTED;
  585.     static final int ERRORED = MediaTracker.ERRORED;
  586.     static final int COMPLETE = MediaTracker.COMPLETE;
  587.  
  588.     static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE);
  589.     static final int DONE = (ABORTED | ERRORED | COMPLETE);
  590.  
  591.     synchronized int getStatus(boolean doLoad) {
  592.     if (doLoad && ((status & LOADSTARTED) == 0)) {
  593.         status = (status & ~ABORTED) | LOADING;
  594.         startLoad();
  595.     }
  596.     return status;
  597.     }
  598.  
  599.     void setStatus(int flag) {
  600.     synchronized (this) {
  601.         status = flag;
  602.     }
  603.     tracker.setDone();
  604.     }
  605. }
  606.  
  607. class ImageMediaEntry extends MediaEntry implements ImageObserver {
  608.     Image image;
  609.     int width;
  610.     int height;
  611.  
  612.     ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) {
  613.     super(mt, c);
  614.     image = img;
  615.     width = w;
  616.     height = h;
  617.     }
  618.  
  619.     Object getMedia() {
  620.     return image;
  621.     }
  622.  
  623.     void startLoad() {
  624.     if (tracker.target.prepareImage(image, width, height, this)) {
  625.         setStatus(COMPLETE);
  626.     }
  627.     }
  628.  
  629.     public boolean imageUpdate(Image img, int infoflags,
  630.                    int x, int y, int w, int h) {
  631.     if ((infoflags & ERROR) != 0) {
  632.         setStatus(ERRORED);
  633.     } else if ((infoflags & ABORT) != 0) {
  634.         setStatus(ABORTED);
  635.     } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) {
  636.         setStatus(COMPLETE);
  637.     }
  638.     return true;
  639.     }
  640. }
  641.